package cn.swust.HuXinMeng.sound;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.TimeUnit;

import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MetaEventListener;
import javax.sound.midi.MetaMessage;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Sequence;
import javax.sound.midi.Sequencer;

public class MidiPlayer implements MetaEventListener {
	public static final int END_OF_TRACK_MESSAGE = 47;

	private Sequencer sequencer;
	private boolean loop;
	private boolean paused;
	
	private String midiPath;
	private final long sleepTime;
	public MidiPlayer() {
		try {
			sequencer = MidiSystem.getSequencer();
			sequencer.open();
			sequencer.addMetaEventListener(this);
		} catch (MidiUnavailableException ex) {
			sequencer = null;
		}
		this.sleepTime = 0;
	}
	
	public MidiPlayer(String midiPath, boolean loop){
		this(midiPath, loop, TimeUnit.SECONDS.toMillis(3));
	}
	
	public MidiPlayer(String midiPath, boolean loop, long sleepTime){
		try {
			sequencer = MidiSystem.getSequencer();
			sequencer.open();
			sequencer.addMetaEventListener(this);
		} catch (MidiUnavailableException ex) {
			sequencer = null;
		}
		this.midiPath = midiPath;
		this.loop = loop;
		this.sleepTime = sleepTime;
	}

	public Sequence getSequence(String filename) {
		return getSequence(getClass().getResourceAsStream(filename));
	}

	public Sequence getSequence(InputStream is) {
		try {
			if (!is.markSupported()) {
				is = new BufferedInputStream(is);
			}
			Sequence s = MidiSystem.getSequence(is);
			is.close();
			return s;
		} catch (InvalidMidiDataException ex) {
			ex.printStackTrace();
			return null;
		} catch (IOException ex) {
			ex.printStackTrace();
			return null;
		}
	}
	
	public void play(){
		Sequence sequence = getSequence(midiPath); 
		play(sequence, loop);
	}

	public void play(Sequence sequence, boolean loop) {
		if (sequencer != null && sequence != null && sequencer.isOpen()) {
			try {
				sequencer.setSequence(sequence);
				sequencer.start();
				this.loop = loop;
			} catch (InvalidMidiDataException ex) {
				ex.printStackTrace();
			}
		}
	}

	@Override
	public void meta(MetaMessage event) {
		if (event.getType() == END_OF_TRACK_MESSAGE) {
			if (sequencer != null && sequencer.isOpen() && loop) {
				sequencer.setTickPosition(0);
				if (sleepTime > 0) {
					try {
						Thread.sleep(sleepTime);
					} catch (InterruptedException e) {
						
					}
				}
				sequencer.start();
			}
		}
	}

	public void stop() {
		if (sequencer != null && sequencer.isOpen()) {
			sequencer.stop();
			sequencer.setMicrosecondPosition(0);
		}
	}

	public void close() {
		if (sequencer != null && sequencer.isOpen()) {
			sequencer.close();
		}
	}

	public Sequencer getSequencer() {
		return sequencer;
	}

	public void setPaused(boolean paused) {
		if (this.paused != paused && sequencer != null && sequencer.isOpen()) {
			this.paused = paused;
			if (paused) {
				sequencer.stop();
			} else {
				sequencer.start();
			}
		}
	}

	public boolean isPaused() {
		return paused;
	}

}
